
<!DOCTYPE html>
<html lang="">


<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
  <meta name="theme-color" content="#202020"/>
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <script src="/js/busuanzi.pure.mini.js" async></script>
  
  
    <meta name="keywords" content="Java,Spring," />
  

  
    <meta name="description" content="一个专注一coding的网站。提供丰富编程知识，包括Spring、Java、EmberJS、SpringBoot等等技术。" />
  
  
  <link rel="icon" type="image/x-icon" href="/image/favicon.ico">
  <title>Spring后置处理器 [ Keep Coding ]</title>
  
    <!-- stylesheets list from config.yml -->
    
      <link rel="stylesheet" href="/css/pure-min.css">
    
      <link rel="stylesheet" href="/css/xoxo.css">
    
  
<meta name="generator" content="Hexo 5.0.2"><link rel="alternate" href="/atom.xml" title="Keep Coding" type="application/atom+xml">
</head>

<body>
  <div class="nav-container">
    <nav class="home-menu pure-menu pure-menu-horizontal">
  <a class="pure-menu-heading" href="/">
    <img class="avatar" src="/image/favicon.ico">
    <span class="title">Keep Coding</span>
  </a>

  <ul class="pure-menu-list clearfix">
      
          
            <li class="pure-menu-item"><a href="/" class="pure-menu-link">首页</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/archives" class="pure-menu-link">归档</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/tags" class="pure-menu-link">标签</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/search" class="pure-menu-link">搜索</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/about" class="pure-menu-link">关于</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/atom.xml" class="pure-menu-link">订阅</a></li>
          
      
  </ul>
   
</nav>
  </div>

  <div class="container" id="content-outer">
    <div class="inner" id="content-inner">
      <div class="post-container">
  <article class="post" id="post">
    <header class="post-header text-center">
      <h1 class="title">
        Spring后置处理器
      </h1>
      <span>
        
        <time class="time" datetime="2020-09-23T14:03:50.185Z">
        2020-09-23
      </time>
        
      </span>
      <span class="slash">/</span>
      <span class="post-meta">
      <span class="post-tags">
        <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/" rel="tag">Java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Spring/" rel="tag">Spring</a></li></ul>
      </span>
    </span>
      <span class="slash">/</span>
      <span class="read">
      <span id="busuanzi_value_page_pv"></span> 点击
    </span>
      <span class="slash">/</span>
      <span class="read">阅读耗时 8 分钟</span>
    </header>

    <div class="post-content">
      <p>本篇来总结汇总Spring各种类型后置处理器的使用。</p>
<h3 id="bean定义后置处理器"><a href="#bean定义后置处理器" class="headerlink" title="bean定义后置处理器"></a>bean定义后置处理器</h3><p>bean定义后置处理器是Spring框架提供的第一个扩展点。其中有两个接口，一个是<code>BeanFactoryPostProcessor</code>接口，一个是<code>BeanDefinitionRegistryPostProcessor</code>接口。</p>
<h4 id="BeanFactroyPostProcessor接口"><a href="#BeanFactroyPostProcessor接口" class="headerlink" title="BeanFactroyPostProcessor接口"></a>BeanFactroyPostProcessor接口</h4><p><code>BeanFactoryPostProcessor</code>接口提供的扩展功能：</p>
<blockquote>
<p><strong>允许用户修改容器中的bean定义信息，调整bean定义属性值。容器会在所有bean定义信加载完毕之后回调此接口，用以修改容器中的bean定义信息</strong>。但是不要在此接口直接通过<code>getBean</code>实例化bean，这样会导致bean过早实例化，违反容器规则导致不可预知的副作用。</p>
<p>如果要实现bean实例化请通过<code>BeanPostProcessor</code>接口实现。</p>
<p>如果有多个<code>BeanFactoryPostProcessor</code>接口并且需要执行它们的执行顺序可以同时实现<code>PriorityOrdered</code>接口或者<code>Ordered</code>接口。</p>
<p>简单讲就是，我们可以通过实现此接口获取到<code>BeanFactory</code>对象（就是参数），操作<code>BeanFactory</code>对象，修改里面的<code>BeanDefinition</code>。<br><strong>但是不要去实例化bean</strong>。</p>
<p>接口的一个典型应用就是<code>PropertySourcesPlaceholderConfigurer</code>。</p>
</blockquote>
<h5 id="接口源码"><a href="#接口源码" class="headerlink" title="接口源码"></a>接口源码</h5><p>Spring框架<code>BeanFactoryPostProcessor</code>接口源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.springframework.beans.factory.config;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.BeansException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 允许自定义修改容器中的bean定义信息，调整bean定义属性值。</span></span><br><span class="line"><span class="comment"> * 容器会在所有bean定义信息加载完毕之后回调此接口，用以修改容器中的bean定义信息。</span></span><br><span class="line"><span class="comment"> * 但是不要在此接口直接通过getBean实例化bean，这样会导致bean过早实例化，违反容器规则导致不可预知的副作用。</span></span><br><span class="line"><span class="comment"> * 如果要实现bean实例化请通过BeanPostProcessor接口。</span></span><br><span class="line"><span class="comment"> * 如果有多个BeanFactoryPostProcessor接口并且需要执行它们的执行顺序可以同时实现PriorityOrdered接口或者Ordered接口。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 简单讲就是，我们可以通过实现此接口获取到BeanFactory对象（就是参数），操作BeanFactory对象，修改里面的BeanDefinition。</span></span><br><span class="line"><span class="comment"> * 但是不要去实例化bean。</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * 另外有一点需要注意的是此接口的实现类会忽略懒加载设置，即使你显式设置了实现类懒加载也是不生效的。</span></span><br><span class="line"><span class="comment"> * 因为Spring需要保证BeanFactoryPostProcessor实现类优先实例化，如果实现类都懒加载了，那么你又如何能修改容器的bean定义呢。。。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> Juergen Hoeller</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> Sam Brannen</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 06.07.2003</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> BeanPostProcessor</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> PropertyResourceConfigurer</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BeanFactoryPostProcessor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在所有的bean定义被加载到容器中后，并且是在所有bean实例化之前就会回调这个接口，</span></span><br><span class="line"><span class="comment">     * 这个接口可以修改容器中的所有bean定义信息，包括重写某些bean的定义属性信息。</span></span><br><span class="line"><span class="comment">     * 比如修改MyServiceImpl为懒加载：beanFactory.getMergedBeanDefinition(MyServiceImpl.class.getName()).setLazyInit(true);</span></span><br><span class="line"><span class="comment">     * 另外一个很典型的应用就是修改bean定义中属性的占位符（PropertySourcesPlaceholderConfigurer），比如读取配置文件把配置文件的配置值注入到类属性上</span></span><br><span class="line"><span class="comment">     * 最常见的就是<span class="doctag">@Value</span>(&quot;$&#123;xxxx&#125;&quot;)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 注意：不要在此接口中实例化bean（就是不要调getBean()方法），提前实例化bean会导致不可预知的结果，</span></span><br><span class="line"><span class="comment">     * 因为目前还处在解析完所有bean定义阶段，bean的实例化（实例化就是根据bean的定义信息创建实例对象）还在后面的阶段。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">postProcessBeanFactory</span><span class="params">(ConfigurableListableBeanFactory beanFactory)</span> <span class="keyword">throws</span> BeansException</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="使用案例"><a href="#使用案例" class="headerlink" title="使用案例"></a>使用案例</h5><p>自定义一个实现类，验证。</p>
<ol>
<li>修改某个bean的定义信息</li>
<li>接口实现来显式设置为懒加载，看是否有效果（正常情况下应该是无效果的，Spring需要保证实现类提前初始化，否则谈何能修改bean定义）。容器启动的过程中就会打印构造方法的日志</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.ubuntuvim.spring.beanfactorypostprocessor;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.BeansException;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.config.BeanFactoryPostProcessor;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.config.ConfigurableListableBeanFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Lazy;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用Spring的一个扩展点，实现BeanFactoryPostProcess接口。</span></span><br><span class="line"><span class="comment"> * 1. 修改某个bean的定义信息</span></span><br><span class="line"><span class="comment"> * 2. 接口实现来显式设置为懒加载，看是否有效果（正常情况下应该是无效果的，Spring需要保证实现类提前初始化，否则谈何能修改bean定义）</span></span><br><span class="line"><span class="comment"> *        容器启动的过程中就会打印构造方法的日志</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 运行结果：</span></span><br><span class="line"><span class="comment"> * com.ubuntuvim.spring.processor.MyBeanFactoryPostProcessorImpl被加载了。。。</span></span><br><span class="line"><span class="comment"> * com.ubuntuvim.spring.bean.LazyLoadingBean被设置成懒加载了。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 没有看到LazyLoadingBean被加载的日志，把beanFactory.getBeanDefinition(beanName).setLazyInit(true);改成false再运行：</span></span><br><span class="line"><span class="comment"> * com.ubuntuvim.spring.processor.MyBeanFactoryPostProcessorImpl被加载了。。。</span></span><br><span class="line"><span class="comment"> * com.ubuntuvim.spring.bean.LazyLoadingBean被设置成懒加载了。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * com.ubuntuvim.spring.bean.LazyLoadingBean被加载了。。。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 可以看到LazyLoadingBean被加载了，完美的验证了前面的两点描述</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: ubuntuvim</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2020/9/23 下午9:17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Lazy</span>  <span class="comment">// 显式指定为懒加载</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBeanFactoryPostProcessorImpl</span> <span class="keyword">implements</span> <span class="title">BeanFactoryPostProcessor</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyBeanFactoryPostProcessorImpl</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;\n&quot;</span> + <span class="keyword">this</span>.getClass().getName() + <span class="string">&quot;被加载了。。。\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postProcessBeanFactory</span><span class="params">(ConfigurableListableBeanFactory beanFactory)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="comment">// @Component注解没有指定名称，所有是默认首字母小写名字</span></span><br><span class="line">        String beanName = <span class="string">&quot;lazyLoadingBean&quot;</span>;</span><br><span class="line">        System.out.println(<span class="string">&quot;\n&quot;</span> + beanName + <span class="string">&quot;被设置成懒加载了。\n&quot;</span>);</span><br><span class="line">        beanFactory.getBeanDefinition(beanName).setLazyInit(<span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>再定义一个普通bean。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.ubuntuvim.spring.beanfactorypostprocessor;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 这个bean在MyBeanFactoryPostProcessorImpl中被设置懒加载了，所以容器启动完毕也会不打印构造方法的日志</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: ubuntuvim</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2020/9/23 下午10:32</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LazyLoadingBean</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LazyLoadingBean</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;\n&quot;</span> + <span class="keyword">this</span>.getClass().getName() + <span class="string">&quot;被加载了。。。\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行结果：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">com.ubuntuvim.spring.beanfactorypostprocessor.MyBeanFactoryPostProcessorImpl被加载了。。。</span><br><span class="line"></span><br><span class="line">lazyLoadingBean被设置成懒加载了。</span><br></pre></td></tr></table></figure>

<p><code>MyBeanFactoryPostProcessorImpl</code>被加载了，<code>LazyLoadingBean</code>没有被加载，把<code>beanFactory.getBeanDefinition(beanName).setLazyInit(true);</code>改成<code>false</code>再运行：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">com.ubuntuvim.spring.beanfactorypostprocessor.MyBeanFactoryPostProcessorImpl被加载了。。。</span><br><span class="line"></span><br><span class="line">lazyLoadingBean被设置成懒加载了。</span><br><span class="line"></span><br><span class="line">com.ubuntuvim.spring.beanfactorypostprocessor.LazyLoadingBean被加载了。。。</span><br></pre></td></tr></table></figure>

<p>可以看到<code>MyBeanFactoryPostProcessorImpl</code>被加载了，<code>LazyLoadingBean</code>也被加载了，完美符合预期。</p>
<h4 id="待跟进"><a href="#待跟进" class="headerlink" title="待跟进"></a>待跟进</h4><p>学习<code>PropertyResourceConfigurer</code>是如何替换类中的占位符<code>@Value(&quot;$&#123;xxx&#125;&quot;)</code>。</p>
<h4 id="BeanDefinitionRegistryPostProcessor接口"><a href="#BeanDefinitionRegistryPostProcessor接口" class="headerlink" title="BeanDefinitionRegistryPostProcessor接口"></a>BeanDefinitionRegistryPostProcessor接口</h4><p><code>BeanDefinitionRegistryPostProcessor</code>接口的提供的扩展功能是：</p>
<blockquote>
<p><code>BeanDefinitionRegistryPostProcessor</code>接口是<code>BeanFactoryPostProcessor</code>接口的子类，它在父类的基础上增加了<code>postProcessBeanDefinitionRegistry()</code>方法。允许用户获取<code>BeanDefinitionRegistry</code>对象，从而可以<strong>通过编码方式动态修改、新增</strong><code>BeanDefinition</code>。</p>
<p>此接口一个非常重要的实现类就是<code>ConfigurationClassPostProcessor</code>，这个类用于解析<code>@Component</code>，<code>@Service</code>，<code>@ComponentScan</code>，<code>@Configuration</code>等注解，把注解对应的类转换成<code>BeanDefinition</code>然后注册到IoC容器中。</p>
</blockquote>
<h5 id="接口源码-1"><a href="#接口源码-1" class="headerlink" title="接口源码"></a>接口源码</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.springframework.beans.factory.support;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.BeansException;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.config.BeanFactoryPostProcessor;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Extension to the standard &#123;<span class="doctag">@link</span> BeanFactoryPostProcessor&#125; SPI, allowing for</span></span><br><span class="line"><span class="comment"> * the registration of further bean definitions &lt;i&gt;before&lt;/i&gt; regular</span></span><br><span class="line"><span class="comment"> * BeanFactoryPostProcessor detection kicks in. In particular,</span></span><br><span class="line"><span class="comment"> * BeanDefinitionRegistryPostProcessor may register further bean definitions</span></span><br><span class="line"><span class="comment"> * which in turn define BeanFactoryPostProcessor instances.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 扩展标准的BeanFactoryPostProcessor SPI，</span></span><br><span class="line"><span class="comment"> * 允许在常规BeanFactoryPostProcessor检测开始之前注册其他的bean定义，特别是，</span></span><br><span class="line"><span class="comment"> * BeanDefinitionRegistryPostProcessor可以注册其他的bean定义，</span></span><br><span class="line"><span class="comment"> * 这些定义反过来可以用于定义BeanFactoryPostProcessor实例。</span></span><br><span class="line"><span class="comment"> * （也就是说可以借此方法往容器中注入bean定义）一个典型的使用就是ConfigurationClassPostProcessor，</span></span><br><span class="line"><span class="comment"> * 这个类用于解析<span class="doctag">@Component</span>，<span class="doctag">@Services</span>，<span class="doctag">@ComponentScan</span>，<span class="doctag">@Configuration</span>等注解，把注解对应的类转换成BeanDefinition然后注册到IoC容器中。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> Juergen Hoeller</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 3.0.1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> org.springframework.context.annotation.ConfigurationClassPostProcessor</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BeanDefinitionRegistryPostProcessor</span> <span class="keyword">extends</span> <span class="title">BeanFactoryPostProcessor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Modify the application context&#x27;s internal bean definition registry after its</span></span><br><span class="line"><span class="comment">     * standard initialization. All regular bean definitions will have been loaded,</span></span><br><span class="line"><span class="comment">     * but no beans will have been instantiated yet. This allows for adding further</span></span><br><span class="line"><span class="comment">     * bean definitions before the next post-processing phase kicks in.</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * 在标准bean初始化前修改、新增bean定义。</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> registry the bean definition registry used by the application context</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> org.springframework.beans.BeansException in case of errors</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">postProcessBeanDefinitionRegistry</span><span class="params">(BeanDefinitionRegistry registry)</span> <span class="keyword">throws</span> BeansException</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="使用案例-1"><a href="#使用案例-1" class="headerlink" title="使用案例"></a>使用案例</h5><p>自定义一个实现类，通过编码的方式往容器注入一个bean定义。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.ubuntuvim.spring.beanfactorypostprocessor;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.BeansException;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.config.ConfigurableListableBeanFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.support.BeanDefinitionRegistry;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.support.RootBeanDefinition;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Lazy;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通过编程方式注册InjectBeanFromPostProcessor</span></span><br><span class="line"><span class="comment"> * 同样的本类设置成懒加载也是无效的</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: ubuntuvim</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2020/7/17 20:43</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Lazy</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBeanDefinitionRegistryPostProcessor</span> <span class="keyword">implements</span> <span class="title">BeanDefinitionRegistryPostProcessor</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyBeanDefinitionRegistryPostProcessor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;\n&quot;</span> + <span class="keyword">this</span>.getClass().getName() + <span class="string">&quot;被加载了。。。\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postProcessBeanDefinitionRegistry</span><span class="params">(BeanDefinitionRegistry registry)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="comment">//编程方式注入一个bean定义</span></span><br><span class="line">        registry.registerBeanDefinition(InjectBeanFromPostProcessor.class.getName(),</span><br><span class="line">                <span class="keyword">new</span> RootBeanDefinition(InjectBeanFromPostProcessor.class));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postProcessBeanFactory</span><span class="params">(ConfigurableListableBeanFactory beanFactory)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">    <span class="comment">// 本方法的功能和BeanFactoryPostProcessor一样。本来就是从BeanFactoryPostProcessor继承过来的。</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>InjectBeanFromPostProcessor</code>并没有使用任何注解，也没有通过其他方式导入。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.ubuntuvim.spring.beanfactorypostprocessor;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通过BeanDefinitionRegistryPostProcessor接口注入本类到容器中。</span></span><br><span class="line"><span class="comment"> * 并没有在类上使用任何注解，也没有通过其他方式导入容器，期望效果是容器启动完毕之后会打印构造方法的日志</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: ubuntuvim</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2020/9/23 下午11:00</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">InjectBeanFromPostProcessor</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">InjectBeanFromPostProcessor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;\n&quot;</span> + <span class="keyword">this</span>.getClass().getName() + <span class="string">&quot;被加载了。。。\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行结果：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">com.ubuntuvim.spring.beanfactorypostprocessor.MyBeanDefinitionRegistryPostProcessor被加载了。。。</span><br><span class="line"></span><br><span class="line">com.ubuntuvim.spring.beanfactorypostprocessor.InjectBeanFromPostProcessor被加载了。。。</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>结果符合预期，<code>InjectBeanFromPostProcessor</code>成功注册到IoC容器中，并且可以被IoC容器实例化。</p>
<p>以上两个接口就是Spring框架提供的第一个扩展点，用于修改为实例化之前的bean定义信息。</p>
<h4 id="SmartInitializingSingleton接口"><a href="#SmartInitializingSingleton接口" class="headerlink" title="SmartInitializingSingleton接口"></a>SmartInitializingSingleton接口</h4><p><strong>这个接口Spring4.1之后才有</strong></p>
<blockquote>
<p><code>SmartInitializingSingleton</code>是spring 4.1中引入的新特效，与<code>InitializingBean</code>的功能类似，都是<strong>bean实例化后执行自定义初始化</strong>，都是属于<a target="_blank" rel="noopener" href="https://blog.csdn.net/alex_xfboy/article/details/51211054">spring bean生命周期</a>的增强。但是，<code>SmartInitializingSingleton</code>的<strong>定义及触发方式方式上有些区别</strong>，它的定义不在当前的bean中（a bean’s local construction phase），它是回调接口（针对<strong>非lazy单例Bean</strong>），回调的操作是由spring事件<code>ContextRefreshedEvent</code>触发。</p>
</blockquote>
<h5 id="接口源码-2"><a href="#接口源码-2" class="headerlink" title="接口源码"></a>接口源码</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.springframework.beans.factory;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 实现该接口后，当所有单例 bean 都初始化完成以后， 容器会回调该接口的方法 afterSingletonsInstantiated。</span></span><br><span class="line"><span class="comment"> * 主要应用场合就是在所有单例 bean 创建完成之后，可以在该回调中做一些事情。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@PostConstruct</span>是最先被执行的，然后是InitializingBean，最后是SmartInitializingSingleton</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 为什么是在当所有单例 bean 都初始化完成以后才执行这个接口的原因直接看源码就知道了：</span></span><br><span class="line"><span class="comment"> * AbstractApplicationContext.refresh() -&gt; finishBeanFactoryInitialization() -&gt; ConfigurableListableBeanFactory.preInstantiateSingletons()</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 但是需要注意：不要再次接口中提前使用容器管理的bean对象，</span></span><br><span class="line"><span class="comment"> * 因为此时直接通过getBean()方法获取到的实例还没通过IoC容器的其他初始化后置处理的增强。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 4.1</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">SmartInitializingSingleton</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 所有单例对象都是实例化完成之后就会回调这个接口实现类的此方法。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">afterSingletonsInstantiated</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>SmartInitializingSingleton</code>接口的实现主要是Spring框架内部使用，目前Spring框架内部已经有差不多30个实现类。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-677ccab99e962d5562335695f80b97a8340.png" alt="SmartInitializingSingleton接口实现类"></p>
<p>一个很典型的应用是<code>EventListenerMethodProcessor</code>类，这个类的作用的是用来对 <code>@EventListener</code> 提供支持.</p>
<p>主要是标注了<code>@EventListener</code> 的方法进行解析, 然后转换为一个 <code>ApplicationListener</code>。解析的方法就是实现了<code>SmartInitializingSingleton</code>接口的<code>afterSingletonsInstantiated()</code>方法，在这个方法中处理。</p>
<h5 id="使用案例-2"><a href="#使用案例-2" class="headerlink" title="使用案例"></a>使用案例</h5><p>定义一个实现类，同时实现了<code>SmartInitializingSingleton</code>接口和<code>InitializingBean</code>接口，并且在类中使用<code>@PostConstruct</code>注解。验证这几种方式的初始化执行顺序。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.ubuntuvim.spring.beanpostprocess;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.BeansException;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.InitializingBean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.SmartInitializingSingleton;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContextAware;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.annotation.PostConstruct;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在所有bean实例化之后（初始化前）回调这个接口afterSingletonsInstantiated</span></span><br><span class="line"><span class="comment"> * 初始化操作执行顺序：<span class="doctag">@PostConstruct</span>是最先被执行的，然后是InitializingBean，最后是SmartInitializingSingleton</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MySmartInitializingSingletonImpl</span> <span class="keyword">implements</span> <span class="title">SmartInitializingSingleton</span>, <span class="title">ApplicationContextAware</span>, <span class="title">InitializingBean</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ApplicationContext applicationContext;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">invokePostConstruct</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;1. @PostConstruct注释方法被执行&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterSingletonsInstantiated</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;3. SmartInitializingSingleton接口的afterSingletonsInstantiated()方法被执行了&quot;</span>);</span><br><span class="line">        InitBean initBean = applicationContext.getBean(InitBean.class);</span><br><span class="line">        initBean.f();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setApplicationContext</span><span class="params">(ApplicationContext applicationContext)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.applicationContext = applicationContext;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;2. InitializingBean接口的afterPropertiesSet()方法被执行了&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行结果：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">1. @PostConstruct注释方法被执行</span><br><span class="line">2. InitializingBean接口的afterPropertiesSet()方法被执行了</span><br><span class="line">3. SmartInitializingSingleton接口的afterSingletonsInstantiated()方法被执行了</span><br><span class="line">com.ubuntuvim.spring.beanpostprocess.InitBean的方法f()被调用</span><br></pre></td></tr></table></figure>




    </div>

      
    <div class="post-nav">
      <div class="post-nav-item post-nav-next">
        
          <span>〈 </span>
          <a href="/2020/09/08/Spring/Spring%E4%BA%8B%E5%8A%A1%E4%BC%A0%E6%92%AD%E6%9C%BA%E5%88%B6/" rel="next" title="Spring事务传播机制">
          Spring事务传播机制
          </a>
        
      </div>
  
      <div class="post-nav-item post-nav-prev">
          
          <a href="/2020/10/19/Spring/Spring%20IoC%E5%8E%9F%E7%90%86/" rel="prev" title="Spring IoC原理">
            Spring IoC原理
          </a>
          <span>〉</span>
        
      </div>
    </div>
  
  </article>
  <div class="toc-container">
    
  <div id="toc" class="toc-article">
    <strong class="toc-title">目录</strong>
    <ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#bean%E5%AE%9A%E4%B9%89%E5%90%8E%E7%BD%AE%E5%A4%84%E7%90%86%E5%99%A8"><span class="toc-text">bean定义后置处理器</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#BeanFactroyPostProcessor%E6%8E%A5%E5%8F%A3"><span class="toc-text">BeanFactroyPostProcessor接口</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%8E%A5%E5%8F%A3%E6%BA%90%E7%A0%81"><span class="toc-text">接口源码</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E6%A1%88%E4%BE%8B"><span class="toc-text">使用案例</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BE%85%E8%B7%9F%E8%BF%9B"><span class="toc-text">待跟进</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#BeanDefinitionRegistryPostProcessor%E6%8E%A5%E5%8F%A3"><span class="toc-text">BeanDefinitionRegistryPostProcessor接口</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%8E%A5%E5%8F%A3%E6%BA%90%E7%A0%81-1"><span class="toc-text">接口源码</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E6%A1%88%E4%BE%8B-1"><span class="toc-text">使用案例</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#SmartInitializingSingleton%E6%8E%A5%E5%8F%A3"><span class="toc-text">SmartInitializingSingleton接口</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%8E%A5%E5%8F%A3%E6%BA%90%E7%A0%81-2"><span class="toc-text">接口源码</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E6%A1%88%E4%BE%8B-2"><span class="toc-text">使用案例</span></a></li></ol></li></ol></li></ol>
  </div>


  </div>
</div>


<div class="copyright">
    <span>本作品采用</span>
    <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by/4.0/">知识共享署名 4.0 国际许可协议</a>
    <span>进行许可。 转载时请注明原文链接。</span>
</div>
<div class="share">

</div>
<div class="post-container">
    <article class="post">
      <div id="container"></div>
    </article>
</div>

<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css">
<script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
<script>
var gitment = new Gitment({
  id: 'Spring后置处理器', // 可选。默认为 location.href
  owner: 'ubuntuvim',
  repo: 'ubuntuvim.github.io',
  oauth: {
    client_id: '48b3df0bf4ba1743e0a3',
    client_secret: 'c6c2cec09acb5eecc9d325d56cef187264aa1e16',
  },
})
gitment.render('container')
</script>



    </div>

    

  </div>
  <footer class="footer text-center">
    <div id="bottom-inner">
        <a class="bottom-item" href="http://xcoding.tech/">首页</a> |
        <a class="bottom-item" href="http://xcoding.tech/" target="_blank">主站</a> |
        <a class="bottom-item" href="https://github.com/ubuntuvim" target="_blank">GitHub</a> |
        <a class="bottom-item" href="https://hexo.io" target="_blank">Powered by hexo</a> |
        <a class="bottom-item" href="https://github.com/KevinOfNeu/hexo-theme-xoxo" target="_blank">Theme xoxo</a>
    </div>
</footer>


  <script src='https://unpkg.com/mermaid@7.1.2/dist/mermaid.min.js'></script>
  <script>
    if (window.mermaid) {
      mermaid.initialize({theme: 'forest'});
    }
  </script>


  
  <!-- scripts list from theme config.yml -->
  
    <script src="/js/zepto.min.js"></script>
  


<script>
  (function(window, document, undefined) {

    var timer = null;

    function returnTop() {
      cancelAnimationFrame(timer);
      timer = requestAnimationFrame(function fn() {
        var oTop = document.body.scrollTop || document.documentElement.scrollTop;
        if (oTop > 0) {
          document.body.scrollTop = document.documentElement.scrollTop = oTop - 50;
          timer = requestAnimationFrame(fn);
        } else {
          cancelAnimationFrame(timer);
        }
      });
    }

    var hearts = [];
    window.requestAnimationFrame = (function() {
      return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback) {
          setTimeout(callback, 1000 / 60);
        }
    })();
    init();

    function init() {
      css(".heart{z-index:9999;width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: absolute;}.heart:after{top: -5px;}.heart:before{left: -5px;}");
      attachEvent();
      gameloop();
      addMenuEvent();
    }

    function gameloop() {
      for (var i = 0; i < hearts.length; i++) {
        if (hearts[i].alpha <= 0) {
          document.body.removeChild(hearts[i].el);
          hearts.splice(i, 1);
          continue;
        }
        hearts[i].y--;
        hearts[i].scale += 0.004;
        hearts[i].alpha -= 0.013;
        hearts[i].el.style.cssText = "left:" + hearts[i].x + "px;top:" + hearts[i].y + "px;opacity:" + hearts[i].alpha + ";transform:scale(" + hearts[i].scale + "," + hearts[i].scale + ") rotate(45deg);background:" + hearts[i].color;
      }
      requestAnimationFrame(gameloop);
    }

    /**
     * 给logo设置点击事件
     * 
     * - 回到顶部
     * - 出现爱心
     */
    function attachEvent() {
      var old = typeof window.onclick === "function" && window.onclick;
      var logo = document.getElementById("logo");
      if (logo) {
        logo.onclick = function(event) {
          returnTop();
          old && old();
          createHeart(event);
        }
      }
      
    }

    function createHeart(event) {
      var d = document.createElement("div");
      d.className = "heart";
      hearts.push({
        el: d,
        x: event.clientX - 5,
        y: event.clientY - 5,
        scale: 1,
        alpha: 1,
        color: randomColor()
      });
      document.body.appendChild(d);
    }

    function css(css) {
      var style = document.createElement("style");
      style.type = "text/css";
      try {
        style.appendChild(document.createTextNode(css));
      } catch (ex) {
        style.styleSheet.cssText = css;
      }
      document.getElementsByTagName('head')[0].appendChild(style);
    }

    function randomColor() {
      // return "rgb(" + (~~(Math.random() * 255)) + "," + (~~(Math.random() * 255)) + "," + (~~(Math.random() * 255)) + ")";
      return "#F44336";
    }

    function addMenuEvent() {
      var menu = document.getElementById('menu-main-post');
      if (menu) {
        var toc = document.getElementById('toc');
        if (toc) {
          menu.onclick = function() {
            if (toc) {
              if (toc.style.display == 'block') {
                toc.style.display = 'none';
              } else {
                toc.style.display = 'block';
              }
            }
          };
        } else {
          menu.style.display = 'none';
        }
      }
    }

  })(window, document);
</script>

  



</body>
</html>
